/** @file

Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

Module Name:

  WinGopInput.c

Abstract:

  This file produces the Simple Text In for an Gop window.

  This stuff is linked at the hip to the Window, since the window
  processing is done in a thread kicked off in WinNtGopImplementation.c

  Since the window information is processed in an other thread we need
  a keyboard Queue to pass data about. The Simple Text In code just
  takes data off the Queue. The WinProc message loop takes keyboard input
  and places it in the Queue.


**/


#include "WinGop.h"


/**
  TODO: Add function description

  @param  Private               TODO: add argument description

  @retval EFI_SUCCESS           TODO: Add description for return value

**/
EFI_STATUS
GopPrivateCreateQ (
  IN  GRAPHICS_PRIVATE_DATA    *Private,
  IN GOP_QUEUE_FIXED           *Queue
  )
{
  InitializeCriticalSection (&Queue->Cs);
  Queue->Front = 0;
  Queue->Rear  = 0;
  return EFI_SUCCESS;
}


/**
  TODO: Add function description

  @param  Private               TODO: add argument description

  @retval EFI_SUCCESS           TODO: Add description for return value

**/
EFI_STATUS
GopPrivateDestroyQ (
  IN  GRAPHICS_PRIVATE_DATA    *Private,
  IN GOP_QUEUE_FIXED           *Queue
  )
{
  Queue->Front = 0;
  Queue->Rear  = 0;
  DeleteCriticalSection (&Queue->Cs);
  return EFI_SUCCESS;
}


/**
  TODO: Add function description

  @param  Private               TODO: add argument description
  @param  Key                   TODO: add argument description

  @retval EFI_NOT_READY         TODO: Add description for return value
  @retval EFI_SUCCESS           TODO: Add description for return value

**/
EFI_STATUS
GopPrivateAddQ (
  IN  GRAPHICS_PRIVATE_DATA    *Private,
  IN GOP_QUEUE_FIXED           *Queue,
  IN EFI_KEY_DATA              *KeyData
  )
{
  EnterCriticalSection (&Queue->Cs);

  if ((Queue->Rear + 1) % MAX_Q == Queue->Front) {
    LeaveCriticalSection (&Queue->Cs);
    return EFI_NOT_READY;
  }

  CopyMem (&Queue->Q[Queue->Rear], KeyData, sizeof (EFI_KEY_DATA));
  Queue->Rear           = (Queue->Rear + 1) % MAX_Q;

  LeaveCriticalSection (&Queue->Cs);
  return EFI_SUCCESS;
}


/**
  TODO: Add function description

  @param  Private               TODO: add argument description
  @param  Key                   TODO: add argument description

  @retval EFI_NOT_READY         TODO: Add description for return value
  @retval EFI_SUCCESS           TODO: Add description for return value

**/
EFI_STATUS
GopPrivateDeleteQ (
  IN  GRAPHICS_PRIVATE_DATA    *Private,
  IN  GOP_QUEUE_FIXED          *Queue,
  OUT EFI_KEY_DATA             *Key
  )
{
  EnterCriticalSection (&Queue->Cs);

  if (Queue->Front == Queue->Rear) {
    LeaveCriticalSection (&Queue->Cs);
    return EFI_NOT_READY;
  }

  CopyMem (Key, &Queue->Q[Queue->Front], sizeof (EFI_KEY_DATA));
  Queue->Front  = (Queue->Front + 1) % MAX_Q;

  if (Key->Key.ScanCode == SCAN_NULL && Key->Key.UnicodeChar == CHAR_NULL) {
    if (!Private->IsPartialKeySupport) {
      //
      // If partial keystrok is not enabled, don't return the partial keystroke.
      //
      LeaveCriticalSection (&Queue->Cs);
      ZeroMem (Key, sizeof (EFI_KEY_DATA));
      return EFI_NOT_READY;
    }
  }
  LeaveCriticalSection (&Queue->Cs);
  return EFI_SUCCESS;
}


/**
  TODO: Add function description

  @param  Private               TODO: add argument description

  @retval EFI_NOT_READY         TODO: Add description for return value
  @retval EFI_SUCCESS           TODO: Add description for return value

**/
EFI_STATUS
GopPrivateCheckQ (
  IN  GOP_QUEUE_FIXED     *Queue
  )
{
  if (Queue->Front == Queue->Rear) {
    return EFI_NOT_READY;
  }

  return EFI_SUCCESS;
}

/**
  Initialize the key state.

  @param  Private               The GOP_PRIVATE_DATA instance.
  @param  KeyState              A pointer to receive the key state information.
**/
VOID
InitializeKeyState (
  IN  GRAPHICS_PRIVATE_DATA    *Private,
  IN  EFI_KEY_STATE            *KeyState
  )
{
  KeyState->KeyShiftState  = EFI_SHIFT_STATE_VALID;
  KeyState->KeyToggleState = EFI_TOGGLE_STATE_VALID;

  //
  // Record Key shift state and toggle state
  //
  if (Private->LeftCtrl) {
    KeyState->KeyShiftState  |= EFI_LEFT_CONTROL_PRESSED;
  }
  if (Private->RightCtrl) {
    KeyState->KeyShiftState  |= EFI_RIGHT_CONTROL_PRESSED;
  }
  if (Private->LeftAlt) {
    KeyState->KeyShiftState  |= EFI_LEFT_ALT_PRESSED;
  }
  if (Private->RightAlt) {
    KeyState->KeyShiftState  |= EFI_RIGHT_ALT_PRESSED;
  }
  if (Private->LeftShift) {
    KeyState->KeyShiftState  |= EFI_LEFT_SHIFT_PRESSED;
  }
  if (Private->RightShift) {
    KeyState->KeyShiftState  |= EFI_RIGHT_SHIFT_PRESSED;
  }
  if (Private->LeftLogo) {
    KeyState->KeyShiftState  |= EFI_LEFT_LOGO_PRESSED;
  }
  if (Private->RightLogo) {
    KeyState->KeyShiftState  |= EFI_RIGHT_LOGO_PRESSED;
  }
  if (Private->Menu) {
    KeyState->KeyShiftState  |= EFI_MENU_KEY_PRESSED;
  }
  if (Private->SysReq) {
    KeyState->KeyShiftState  |= EFI_SYS_REQ_PRESSED;
  }
  if (Private->CapsLock) {
    KeyState->KeyToggleState |= EFI_CAPS_LOCK_ACTIVE;
  }
  if (Private->NumLock) {
    KeyState->KeyToggleState |= EFI_NUM_LOCK_ACTIVE;
  }
  if (Private->ScrollLock) {
    KeyState->KeyToggleState |= EFI_SCROLL_LOCK_ACTIVE;
  }
  if (Private->IsPartialKeySupport) {
    KeyState->KeyToggleState |= EFI_KEY_STATE_EXPOSED;
  }
}

/**
  TODO: Add function description

  @param  Private               TODO: add argument description
  @param  Key                   TODO: add argument description

  @retval EFI_NOT_READY         TODO: Add description for return value
  @retval EFI_SUCCESS           TODO: Add description for return value

**/
EFI_STATUS
GopPrivateAddKey (
  IN  GRAPHICS_PRIVATE_DATA  *Private,
  IN  EFI_INPUT_KEY          Key
  )
{
  EFI_KEY_DATA            KeyData;

  KeyData.Key = Key;
  InitializeKeyState (Private, &KeyData.KeyState);

  //
  // Convert Ctrl+[1-26] to Ctrl+[A-Z]
  //
  if ((Private->LeftCtrl || Private->RightCtrl) &&
      (KeyData.Key.UnicodeChar >= 1) && (KeyData.Key.UnicodeChar <= 26)
     ) {
    if ((Private->LeftShift || Private->RightShift) == Private->CapsLock) {
      KeyData.Key.UnicodeChar = (CHAR16)(KeyData.Key.UnicodeChar + L'a' - 1);
    } else {
      KeyData.Key.UnicodeChar = (CHAR16)(KeyData.Key.UnicodeChar + L'A' - 1);
    }
  }

  //
  // Unmask the Shift bit for printable char
  //
  if (((KeyData.Key.UnicodeChar >= L'a') && (KeyData.Key.UnicodeChar <= L'z')) ||
      ((KeyData.Key.UnicodeChar >= L'A') && (KeyData.Key.UnicodeChar <= L'Z'))
     ) {
    KeyData.KeyState.KeyShiftState &= ~(EFI_LEFT_SHIFT_PRESSED | EFI_RIGHT_SHIFT_PRESSED);
  }

  GopPrivateAddQ (Private, &Private->QueueForRead, &KeyData);
  if (Private->MakeRegisterdKeyCallback != NULL) {
    Private->MakeRegisterdKeyCallback (Private->RegisterdKeyCallbackContext, &KeyData);
  }

  return EFI_SUCCESS;
}


EFI_STATUS
EFIAPI
WinNtWndCheckKey (
  IN  EMU_GRAPHICS_WINDOW_PROTOCOL *GraphicsIo
  )
{
  GRAPHICS_PRIVATE_DATA           *Private;

  Private = GRAPHICS_PRIVATE_DATA_FROM_THIS (GraphicsIo);

  return GopPrivateCheckQ (&Private->QueueForRead);

}
EFI_STATUS
EFIAPI
WinNtWndGetKey (
  IN  EMU_GRAPHICS_WINDOW_PROTOCOL  *GraphicsIo,
  IN  EFI_KEY_DATA                  *KeyData
  )
/*++

  Routine Description:
    Reads the next keystroke from the input device. The WaitForKey Event can
    be used to test for existance of a keystroke via WaitForEvent () call.

  Arguments:
    Private    - The private structure of WinNt Gop device.
    KeyData    - A pointer to a buffer that is filled in with the keystroke
                 state data for the key that was pressed.

  Returns:
    EFI_SUCCESS           - The keystroke information was returned.
    EFI_NOT_READY         - There was no keystroke data availiable.
    EFI_DEVICE_ERROR      - The keystroke information was not returned due to
                            hardware errors.
    EFI_INVALID_PARAMETER - KeyData is NULL.

--*/
{
  EFI_STATUS                      Status;
  GRAPHICS_PRIVATE_DATA           *Private;

  Private = GRAPHICS_PRIVATE_DATA_FROM_THIS (GraphicsIo);

  ZeroMem (&KeyData->Key, sizeof (KeyData->Key));
  InitializeKeyState (Private, &KeyData->KeyState);

  Status  = GopPrivateCheckQ (&Private->QueueForRead);
  if (!EFI_ERROR (Status)) {
    //
    // If a Key press exists try and read it.
    //
    Status = GopPrivateDeleteQ (Private, &Private->QueueForRead, KeyData);
    if (!EFI_ERROR (Status)) {
      //
      // If partial keystroke is not enabled, check whether it is value key. If not return
      // EFI_NOT_READY.
      //
      if (!Private->IsPartialKeySupport) {
        if (KeyData->Key.ScanCode == SCAN_NULL && KeyData->Key.UnicodeChar == CHAR_NULL) {
          Status = EFI_NOT_READY;
        }
      }
    }
  }

  return Status;

}

EFI_STATUS
EFIAPI
WinNtWndKeySetState (
  IN EMU_GRAPHICS_WINDOW_PROTOCOL   *GraphicsIo,
  IN EFI_KEY_TOGGLE_STATE           *KeyToggleState
  )
{
  GRAPHICS_PRIVATE_DATA           *Private;

  Private = GRAPHICS_PRIVATE_DATA_FROM_THIS (GraphicsIo);
  Private->ScrollLock = FALSE;
  Private->NumLock = FALSE;
  Private->CapsLock = FALSE;
  Private->IsPartialKeySupport = FALSE;

  if ((*KeyToggleState & EFI_SCROLL_LOCK_ACTIVE) == EFI_SCROLL_LOCK_ACTIVE) {
    Private->ScrollLock = TRUE;
  }
  if ((*KeyToggleState & EFI_NUM_LOCK_ACTIVE) == EFI_NUM_LOCK_ACTIVE) {
    Private->NumLock = TRUE;
  }
  if ((*KeyToggleState & EFI_CAPS_LOCK_ACTIVE) == EFI_CAPS_LOCK_ACTIVE) {
    Private->CapsLock = TRUE;
  }
  if ((*KeyToggleState & EFI_KEY_STATE_EXPOSED) == EFI_KEY_STATE_EXPOSED) {
    Private->IsPartialKeySupport = TRUE;
  }
  Private->KeyState.KeyToggleState = *KeyToggleState;
  return EFI_SUCCESS;
}


EFI_STATUS
EFIAPI
WinNtWndRegisterKeyNotify (
  IN EMU_GRAPHICS_WINDOW_PROTOCOL                        *GraphicsIo,
  IN EMU_GRAPHICS_WINDOW_REGISTER_KEY_NOTIFY_CALLBACK    MakeCallBack,
  IN EMU_GRAPHICS_WINDOW_REGISTER_KEY_NOTIFY_CALLBACK    BreakCallBack,
  IN VOID                                                *Context
  )
{
  GRAPHICS_PRIVATE_DATA           *Private;

  Private = GRAPHICS_PRIVATE_DATA_FROM_THIS (GraphicsIo);

  Private->MakeRegisterdKeyCallback    = MakeCallBack;
  Private->BreakRegisterdKeyCallback   = BreakCallBack;
  Private->RegisterdKeyCallbackContext = Context;

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
WinNtWndCheckPointer (
  IN  EMU_GRAPHICS_WINDOW_PROTOCOL *GraphicsIo
  )
{
  GRAPHICS_PRIVATE_DATA           *Private;

  Private = GRAPHICS_PRIVATE_DATA_FROM_THIS (GraphicsIo);

  return EFI_NOT_READY;
}


EFI_STATUS
EFIAPI
WinNtWndGetPointerState (
  IN  EMU_GRAPHICS_WINDOW_PROTOCOL  *GraphicsIo,
  IN  EFI_SIMPLE_POINTER_STATE      *State
  )
{
  GRAPHICS_PRIVATE_DATA           *Private;

  Private = GRAPHICS_PRIVATE_DATA_FROM_THIS (GraphicsIo);

  return EFI_NOT_READY;
}
